// MRTObject.cpp: implementation of the CRectObject class.
//
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "MRTObject.h"
#include "XMLSettings.h"
#include "Line.h"
#include "Bezier.h"
#ifndef _TOOLMAKER
#include "RemarkObject.h"
#include "meta.h"
#endif
#include "SaveAsBmp.h"

#include "MyMEMDC.h"

#ifdef _VIWCONTROL
#include "resource.h"
#include "MRTPropSheet.h"
#include "ChartObject.h"
#endif

CString g_strPatPath;
void InitPatPath()
{
	TCHAR szPath[_MAX_PATH];
	GetModuleFileName(NULL, szPath, _MAX_PATH);
	g_strPatPath = szPath;
	g_strPatPath = g_strPatPath.Left(g_strPatPath.ReverseFind('\\'));
	g_strPatPath = g_strPatPath.Left(g_strPatPath.ReverseFind('\\'));
	g_strPatPath += _T("\\Template\\");
}

CBrush* SelectPatternBrush(CDC* pDC, CBrush* pBrush, LPCTSTR pszPattern, COLORREF clrFore, COLORREF clrBkg)
{	
	CString strPattern = pszPattern;
	HBITMAP hFGBmp = (HBITMAP)LoadImage(NULL, strPattern, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
	HBITMAP hBKBmp = (HBITMAP)LoadImage(NULL, strPattern, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
	CRect rcBmp;
	BITMAP bm;
	ZeroMemory(&rcBmp, sizeof(CRect));
	::GetObject(hFGBmp, sizeof(BITMAP), &bm);
	rcBmp.right = bm.bmWidth;
	rcBmp.bottom = bm.bmHeight;

	CRect rcDst = rcBmp;
	if (pDC->IsPrinting())
	{
		int nPrintX = GetDeviceCaps(pDC->GetSafeHdc(), LOGPIXELSX);
		int nPrintY = GetDeviceCaps(pDC->GetSafeHdc(), LOGPIXELSY);

		HDC hDC = GetDC(NULL);
		int nX = GetDeviceCaps(hDC, LOGPIXELSX);
		int nY = GetDeviceCaps(hDC, LOGPIXELSY);
		// TRACE("P - D : %d, %d : %d, %d\n", nPrintX, nPrintY, nX, nY);
		ReleaseDC(NULL, hDC);

		rcDst.right = (double) rcDst.right * (double) nPrintX /
			(double) nX;
		rcDst.bottom = (double) rcDst.bottom * (double) nPrintY /
			(double) nY;
	}
	
	HDC hMemDC1 = ::CreateCompatibleDC(pDC->GetSafeHdc());
	HBITMAP hBitmap = ::CreateCompatibleBitmap(pDC->GetSafeHdc(),
								rcDst.Width(), rcDst.Height());
	HBITMAP hOldBMP1 = (HBITMAP) ::SelectObject(hMemDC1, hBitmap);
	FillRect(hMemDC1, &rcDst, CBrush(clrBkg));
	
	HDC hFGDC = ::CreateCompatibleDC(hMemDC1);
	HBITMAP hFGBitmap = ::CreateCompatibleBitmap(hMemDC1,
		rcDst.Width(), rcDst.Height());			
	HBITMAP hFGOldBitmap = (HBITMAP) ::SelectObject(hFGDC, hFGBitmap);
	FillRect(hFGDC, &rcDst, CBrush(clrFore));		
	
	HDC hTmpDC = ::CreateCompatibleDC(hFGDC);	
	HBITMAP hTmpBMP = ::CreateCompatibleBitmap(hFGDC, rcDst.Width(),
								rcDst.Height());
	HBITMAP hTmpOldBMP = (HBITMAP) ::SelectObject(hTmpDC, hTmpBMP);
	
	HDC hMemDC2 = ::CreateCompatibleDC(hTmpDC);
	HBITMAP hOldBMP2 = (HBITMAP) ::SelectObject(hMemDC2, hFGBmp);
	
	HDC hMemDC3 = ::CreateCompatibleDC(hMemDC1);
	HBITMAP hOldBMP3 = (HBITMAP) ::SelectObject(hMemDC3, hBKBmp);
	
	::StretchBlt(hMemDC1, 0, 0, rcDst.Width(), rcDst.Height(),
		hMemDC3, rcBmp.left, rcBmp.top, rcBmp.Width(), rcBmp.Height(),
		SRCAND);
	::StretchBlt(hTmpDC, 0, 0, rcDst.Width(), rcDst.Height(), hMemDC2,
		rcBmp.left, rcBmp.top, rcBmp.Width(), rcBmp.Height(),
		NOTSRCCOPY);
	::BitBlt(hFGDC, 0, 0, rcDst.Width(), rcDst.Height(), hTmpDC,
		rcBmp.left, rcBmp.top, SRCAND);
	::BitBlt(hMemDC1, 0, 0, rcDst.Width(), rcDst.Height(), hFGDC,
		rcBmp.left, rcBmp.top, SRCPAINT);
	
	LOGBRUSH logBrush;
	
	logBrush.lbStyle = BS_PATTERN;
	logBrush.lbColor = clrFore;  
	logBrush.lbHatch = (LONG) hBitmap;

	CBrush* pOldBr = NULL;
	if (pBrush->CreateBrushIndirect(&logBrush))
		pOldBr = pDC->SelectObject(pBrush);
	else
	{
		DWORD dwError = GetLastError();
		// TRACE("Create Brush Err : %d\n", dwError);
		pOldBr = (CBrush*)pDC->SelectStockObject(NULL_BRUSH);
	}

	DeleteObject(::SelectObject(hFGDC, hFGOldBitmap));
	DeleteObject(::SelectObject(hTmpDC, hTmpOldBMP));
	DeleteObject(::SelectObject(hMemDC1, hOldBMP1));
	DeleteObject(::SelectObject(hMemDC2, hOldBMP2));
	DeleteObject(::SelectObject(hMemDC3, hOldBMP3));
	DeleteObject(hBitmap);
	DeleteObject(hBKBmp);
	DeleteObject(hFGBmp);
	
	DeleteDC(hFGDC);
	DeleteDC(hTmpDC);
	DeleteDC(hMemDC1);
	DeleteDC(hMemDC2);
	DeleteDC(hMemDC3);
	
	
	return pOldBr;
}

void DrawPattern(CDC* pDC, LPRECT lpRect, LPCTSTR pszPattern, COLORREF clrFore, COLORREF clrBkg)
{
	if (g_strPatPath.IsEmpty())
		InitPatPath();
	
	if ((lpRect->top == lpRect->bottom)||(lpRect->left==lpRect->right))
		return;
	
	CString strPattern = g_strPatPath + pszPattern;
#ifndef _TOOLMAKER
	if (strPattern.Right(4).CompareNoCase(_T(".rmk")) == 0)
	{
		CRemarkObject obj;
		if (obj.LoadFromFile(strPattern, FALSE))
		{
			obj.Draw(pDC, lpRect);
		}
		return;
	}
#endif
	if (strPattern.Right(4).CompareNoCase(_T(".bmp")))
		strPattern = g_strPatPath + _T("Graphic\\") + pszPattern + _T(".bmp");

	CBrush br;
	if (pDC->IsPrinting())
	{
		HDC hDC = GetDC(NULL);
		CDC* pDrawDC = CDC::FromHandle(hDC);
		CRect rcDest = lpRect;
		CRect rcDraw = lpRect;
		int nMM = pDrawDC->SetMapMode(MM_LOMETRIC);
		pDrawDC->LPtoDP(&rcDraw);
		pDrawDC->SetMapMode(nMM);
		
		CMyMemDC dcMem(pDrawDC, &rcDraw);
		CBrush* pOldBr = SelectPatternBrush(&dcMem, &br, strPattern, clrFore, clrBkg);
		dcMem.FillRect(rcDraw, &br);
		dcMem.SelectObject(&dcMem.m_oldBitmap);
		PrintBitmap(pDC, dcMem.m_bitmap, rcDest);
		dcMem.m_pDC = NULL;
		ReleaseDC(NULL, hDC);
		return;
	}

	CBrush* pOldBr = SelectPatternBrush(pDC, &br, strPattern, clrFore, clrBkg);
	pDC->FillRect(lpRect, &br);
	pDC->SelectObject(pOldBr);
}

#pragma warning(disable:4244)

// the struct below is used to determine the qualities of a particular handle
struct AFX_HANDLEINFO
{
	size_t nOffsetX;    // offset within RECT for X coordinate
	size_t nOffsetY;    // offset within RECT for Y coordinate
	int nCenterX;       // adjust X by Width()/2 * this number
	int nCenterY;       // adjust Y by Height()/2 * this number
	int nHandleX;       // adjust X by handle size * this number
	int nHandleY;       // adjust Y by handle size * this number
	int nInvertX;       // handle converts to this when X inverted
	int nInvertY;       // handle converts to this when Y inverted
};

// this array describes all 8 handles (clock-wise)
AFX_STATIC_DATA const AFX_HANDLEINFO _afxHandleInfo[] =
{
	// corner handles (top-left, top-right, bottom-right, bottom-left
	{ offsetof(RECT, left), offsetof(RECT, top),        0, 0,  0,  0, 1, 3 },
	{ offsetof(RECT, right), offsetof(RECT, top),       0, 0, -1,  0, 0, 2 },
	{ offsetof(RECT, right), offsetof(RECT, bottom),    0, 0, -1, -1, 3, 1 },
	{ offsetof(RECT, left), offsetof(RECT, bottom),     0, 0,  0, -1, 2, 0 },

	// side handles (top, right, bottom, left)
	{ offsetof(RECT, left), offsetof(RECT, top),        1, 0,  0,  0, 4, 6 },
	{ offsetof(RECT, right), offsetof(RECT, top),       0, 1, -1,  0, 7, 5 },
	{ offsetof(RECT, left), offsetof(RECT, bottom),     1, 0,  0, -1, 6, 4 },
	{ offsetof(RECT, left), offsetof(RECT, top),        0, 1,  0,  0, 5, 7 }
};


CPtrList CMRTObject::m_lstClass;

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
void CMRTObject::InitClasses()
{
	if (m_lstClass.GetCount() < 1)
	{
		m_lstClass.AddTail(RUNTIME_CLASS(CRectObject));
#ifdef _VIWCONTROL
		// m_lstClass.AddTail(RUNTIME_CLASS(CChartObject));
		m_lstClass.AddTail(RUNTIME_CLASS(CChartWHoriObj));
		m_lstClass.AddTail(RUNTIME_CLASS(CChartWVertObj));
		m_lstClass.AddTail(RUNTIME_CLASS(CChartWSolidObj));
		m_lstClass.AddTail(RUNTIME_CLASS(CChartWTableObj));
		m_lstClass.AddTail(RUNTIME_CLASS(CChartWDepthObj));
		m_lstClass.AddTail(RUNTIME_CLASS(CChartWInfoObj));
		m_lstClass.AddTail(RUNTIME_CLASS(CChartThCQObj));
		m_lstClass.AddTail(RUNTIME_CLASS(CChartThHBObj));
		m_lstClass.AddTail(RUNTIME_CLASS(CChartWTargetObj));
#endif
	}
}

CMRTObject* CMRTObject::CreateObj(LPCTSTR pszClassName)
{
	InitClasses();
	
	CString strObjClass = pszClassName;
	CMRTObject* pObj = NULL;
	for (POSITION pos = m_lstClass.GetHeadPosition(); pos != NULL; )
	{
		CRuntimeClass* pRC = (CRuntimeClass*)m_lstClass.GetNext(pos);
		if (strObjClass == pRC->m_lpszClassName)
		{
			pObj = (CMRTObject*)pRC->CreateObject();
			return pObj;
		}
	}

	return pObj;
}

CMRTObject* CMRTObject::CreateObj(CXMLSettings& xml)
{
	CString strObjType, strObjClass;
	xml.Read(_T("ObjType"), strObjType);
	xml.Read(_T("ObjClass"), strObjClass);

	CMRTObject* pObj = NULL;
	if (strObjType == _T("MRTObj"))
	{
		pObj = new CMRTObject;
	}
	else if (strObjType == _T("LineObj"))
	{
		pObj = new CLineObject;
	}
	else if (strObjType == _T("PolylineObj"))
	{
		pObj = new CPolylineObject;
	}
	else if (strObjType == _T("PolygonObj"))
	{
		pObj = new CPolygonObject;
	}
	else if (strObjType == _T("EllipseObj"))
	{
		pObj = new CEllipseObject;
	}
    else if (strObjType == _T("PictureObj"))
    {
        pObj = new CPictureObject(); 
    }
	else if (strObjClass.IsEmpty())
	{
		pObj = new CRectObject;
	}
	else
	{
		pObj = CreateObj(strObjClass);
		if (NULL == pObj)
			return pObj;
	}

	pObj->Serialize(xml);
	return pObj;
}

IMPLEMENT_SERIAL(CMRTObject, CObject, 0)

CMRTObject::CMRTObject()
{
	m_dwProp = 0;
	m_dwLocked = 0;
	m_rectPosition.SetRectEmpty();
	m_nLayer = 1;
}

CMRTObject::~CMRTObject()
{
	while (m_lstChidren.GetCount())
		delete m_lstChidren.RemoveHead();
}

void CMRTObject::Serialize(CArchive& ar)
{
	if (ar.IsStoring())
	{
		CRuntimeClass* pRC = GetRuntimeClass();
		CXMLSettings xml(FALSE, pRC->m_lpszClassName);
		Serialize(xml);
		CString strBuffer;
		xml.WriteXMLToBuffer(strBuffer);
		ar << strBuffer;
	}
	else
	{
		CString strBuffer;
		ar >> strBuffer;
		CXMLSettings xml;
		if (xml.ReadXMLFromFile(strBuffer))
			Serialize(xml);
	}
}

void CMRTObject::Serialize(CXMLSettings& xml, DWORD dwProp /* = 0xFFFFFFFF */)
{
	POSITION pos = m_lstChidren.GetHeadPosition();
	if (xml.IsStoring())
	{
		xml.Write(_T("ObjType"), GetObjType());
		
		CRuntimeClass* pRC = GetRuntimeClass();
		xml.Write(_T("ObjClass"), pRC->m_lpszClassName);

		xml.Write(_T("Layer"), m_nLayer);
		xml.Write(_T("Rect"), m_rectPosition);
		CString strText = m_strText;
		strText.Replace(_T("\r\n"), _T("\r"));
		strText.Replace(_T("\r"), _T("ch(10)"));
		xml.Write(_T("Text"), strText);
		xml.Write(_T("Children"), m_lstChidren.GetCount());
		for (UINT i = 0; pos != NULL; i++)
		{
			CMRTObject* pObj = m_lstChidren.GetNext(pos);
			if (xml.CreateKey(_T("Child%d"), i))
			{
				pObj->Serialize(xml);
				xml.Back();
			}
		}
		xml.Write(_T("Locked"), m_dwLocked);
	}
	else
	{
		if (dwProp & 0x0001)
		{
			xml.Read(_T("Layer"), m_nLayer);
			xml.Read(_T("Rect"), m_rectPosition);
		}

		if (dwProp & 0x0002)
		{
			xml.Read(_T("Text"), m_strText);
			// m_strText.Replace(_T("\r"), _T("\r\n"));
			m_strText.Replace(_T("ch(10)"), _T("\r\n"));
		}

		if (dwProp & 0x0004)
		{
			int iCount = 0;
			xml.Read(_T("Children"), iCount);
			for (UINT i = 0; i < iCount; i++)
			{
				if (xml.Open(_T("Child%d"), i))
				{
					CMRTObject* pObj = CMRTObject::CreateObj(xml);
					m_lstChidren.AddTail(pObj);
					xml.Back();
				}
			}
		}

		if (dwProp & 0x0008)
		{
			xml.Read(_T("Locked"), m_dwLocked);
		}
	}
}

int CMRTObject::EditProp()
{
#ifdef _VIWCONTROL
	CMRTPropSheet sheet(IDS_PROPERTY, this);
	return sheet.DoModal();
#else
	return IDCANCEL;
#endif
}

void CMRTObject::Draw(CDC* pDC, CRect& rect)
{
	POSITION pos = m_lstChidren.GetTailPosition();
	while (pos != NULL)
	{
		CMRTObject* pObj = m_lstChidren.GetPrev(pos);
		
		CRect rcObj = pObj->GetRect();
		rcObj.left += rect.left;
		rcObj.top += rect.top;
		rcObj.right = rcObj.left + pObj->m_rectPosition.Width();
		rcObj.bottom = rcObj.top + pObj->m_rectPosition.Height();
		pObj->Draw(pDC, rcObj);
	}
}

void CMRTObject::SetDoc(IULFile* pDoc)
{
	POSITION pos = m_lstChidren.GetTailPosition();
	while (pos != NULL)
	{
		CMRTObject* pObj = m_lstChidren.GetPrev(pos);
		pObj->SetDoc(pDoc);
	}
}

int CMRTObject::HitTest(CPoint point, CDC* pDC, CRectTracker* pRectTracker)
{
	if (pRectTracker != NULL)
	{
		pRectTracker->m_rect.CopyRect(m_rectPosition);
		pDC->LPtoDP(pRectTracker->m_rect);
		return pRectTracker->HitTest(point);
	}
	return -1;
}

void CMRTObject::GetHandleRect(int nHandle, CRect* pHandleRect, CDC* pDC)
{
	// get normalized rectangle of the tracker
	CRect rectT = *pHandleRect;

	// rectT.NormalizeRect();
	// if ((m_nStyle & (solidLine|dottedLine)) != 0)
		rectT.InflateRect(+1, +1);

	// since the rectangle itself was normalized, we also have to invert the
	//  resize handles.
	// nHandle = NormalizeHit(nHandle);

	// handle case of resize handles outside the tracker
	int size = 5;
	// if (m_nStyle & resizeOutside)
		rectT.InflateRect((size-1)/2, (size-1)/2);

	// calculate position of the resize handle
	int nWidth = rectT.Width();
	int nHeight = rectT.Height();
	CRect rect;
	const AFX_HANDLEINFO* pHandleInfo = &_afxHandleInfo[nHandle];
	rect.left = *(int*)((BYTE*)&rectT + pHandleInfo->nOffsetX);
	rect.top = *(int*)((BYTE*)&rectT + pHandleInfo->nOffsetY);
	rect.left += size * pHandleInfo->nHandleX;
	rect.top += size * pHandleInfo->nHandleY;
	rect.left += pHandleInfo->nCenterX * (nWidth - size) / 2;
	rect.top += pHandleInfo->nCenterY * (nHeight - size) / 2;
	rect.right = rect.left + size;
	rect.bottom = rect.top + size;

	*pHandleRect = rect;
}

void CMRTObject::DrawHandle(CDC* pDC, CRect& rect)
{
	CBrush br(RGB(0, 255, 0));
	CBrush* pOldBr = pDC->SelectObject(&br);
	if (GetObjType() != _T("LineObj"))
		rect.NormalizeRect();
	int nHandles = GetHandleCount();
	for (int i = 0; i < nHandles; ++i)
	{
		CRect rectHandle = rect;
		GetHandleRect(i, &rectHandle, pDC);
		// pDC->FillSolidRect(rect, RGB(0, 0, 0));
		pDC->Rectangle(rectHandle);
	}
}

int CMRTObject::HitTestLine(CPoint point, CPoint ptStart, CPoint ptEnd)
{
	int nHandleSize = 14;

	CRect rect;
	rect.left = ptStart.x - nHandleSize/2;
    rect.top = ptStart.y - nHandleSize/2;
    rect.right = rect.left + nHandleSize;
    rect.bottom = rect.top + nHandleSize;

    if (rect.PtInRect(point))
        return 0;

	rect.left = ptEnd.x - nHandleSize/2;
    rect.top = ptEnd.y - nHandleSize/2;
    rect.right = rect.left + nHandleSize;
    rect.bottom = rect.top + nHandleSize;
    if (rect.PtInRect(point))
        return 2;

    CRect VicX(ptStart, ptEnd);
	rect = VicX;
	
	VicX.NormalizeRect();
    CRect VicY = VicX;

	int nVicinitySize = nHandleSize*2;
    VicX.InflateRect(nVicinitySize, 0);
    VicY.InflateRect(0, nVicinitySize);

    if (!VicX.PtInRect(point) && !VicY.PtInRect(point))
        return -1;

    // check distance from the point to the line by x and by y
    int dx = rect.Width();
    int dy = rect.Height();
    double r = sqrt((double)(dx*dx + dy*dy));

    // compute coordinates relative to the origin of the line
    int x1 = point.x - rect.left;
    int y1 = point.y - rect.top;

    // compute coordinates relative to the line
    double x2 = (x1*dx + y1*dy)/r;
    double y2 = (-x1*dy + y1*dx)/r;

    if ((x2>=0) && (x2<=r) && (y2<=nVicinitySize) && (y2 >= -nVicinitySize))
        return 8;

    return -1;
}

void CMRTObject::SetRect(LPCRECT lpSrcRect)
{
	if (!IsLocked())
	{
		if (m_lstChidren.GetCount())
		{
			CRect rect = m_rectPosition;
			m_rectPosition = lpSrcRect;
			double fX = (double) abs(m_rectPosition.Width()) /
				(double) abs(rect.Width());
			double fY = (double) abs(m_rectPosition.Height()) /
				(double) abs(rect.Height());
			POSITION pos = m_lstChidren.GetHeadPosition();
			for (; pos != NULL;)
			{
				CMRTObject* pObj = m_lstChidren.GetNext(pos);
				rect = pObj->GetRect();
				rect.left = floor((double) rect.left * fX + .5);
				rect.right = floor((double) rect.right * fX + .5);
				rect.top = floor((double) rect.top * fY + .5);
				rect.bottom = floor((double) rect.bottom * fY + .5);
				pObj->SetRect(rect);
			}
		}
		else
			m_rectPosition = lpSrcRect;
	}
}

IMPLEMENT_SERIAL(CLineObject, CMRTObject, 0)

CLineObject::CLineObject()
{
	m_dwProp = 0x0010;
	m_iLineWidth = 1;
	m_iLineStyle = 0;
	m_clrLine = RGB(0, 0, 0);
	m_dwArrow = 0;
	m_iArrowSize = 20;
}

CLineObject::~CLineObject()
{
}

void CLineObject::Serialize(CXMLSettings& xml, DWORD dwProp /* = 0xFFFFFFFF */)
{
	CMRTObject::Serialize(xml, dwProp);
	if (xml.IsStoring())
	{
		xml.Write(_T("LineColor"), m_clrLine);
		xml.Write(_T("LWidth"), m_iLineWidth);
		xml.Write(_T("LStyle"), m_iLineStyle);
		xml.Write(_T("LArrow"), m_dwArrow);
	}
	else
	{
		if (dwProp & 0x0010)
		{
			xml.Read(_T("LineColor"), m_clrLine);
			xml.Read(_T("LWidth"), m_iLineWidth);
			xml.Read(_T("LStyle"), m_iLineStyle);
			xml.Read(_T("LArrow"), m_dwArrow);
		}
	}
}

void CLineObject::Draw(CDC* pDC, CRect& rect)
{
	unsigned pattern[8];
	int w = pDC->IsPrinting() ? LPWidth(m_iLineWidth) : LVWidth(m_iLineWidth);
	CDashLine dl(*pDC, pattern, CDashLine::GetPattern(pattern, 0, w,
									m_iLineStyle));
	CPen* pPen = dl.GetRightPen(m_iLineStyle, w, m_clrLine);
	CPen* pOldPen = pDC->SelectObject(pPen);
	dl.MoveTo(rect.TopLeft());
	dl.LineTo(rect.BottomRight());

	if (m_dwArrow)
	{
		CArray<CPoint, CPoint> ptArrow;
		ptArrow.SetSize((m_dwArrow & DA_KX) ? 4 : 3);

		CPen pen(PS_SOLID, 1, m_clrLine);
		if (w > 8)
			m_iArrowSize = 40;
		else if (w > 4)
			m_iArrowSize = 30;
		else
			m_iArrowSize = 20;

		pDC->SelectObject(&pen);
		CBrush brArrow(m_clrLine);
		CBrush* pOldBr = pDC->SelectObject(&brArrow);
		for (int i = 1; i < 3; i++)
		{
			if (m_dwArrow & i)
			{
				DWORD dwArrow = i + DA_V;
				GetArrow(ptArrow.GetData(), dwArrow, rect.TopLeft(),
					rect.BottomRight());
				if (m_dwArrow & DA_AK)
					pDC->Polyline(ptArrow.GetData(), ptArrow.GetSize());
				else
					pDC->Polygon(ptArrow.GetData(), ptArrow.GetSize());
			}
		}

		pDC->SelectObject(pOldBr);
	}

	pDC->SelectObject(pOldPen);
	delete pPen;
}

CPoint CLineObject::GetPointByLength(CPoint ptStart, double K, int nLen)
{
	CPoint ptRes = ptStart; 
	double alfa = ::atan(K); 
	ptRes.x += (int) (nLen * cos(alfa) + 0.5); 
	ptRes.y += (int) (nLen * sin(alfa) + 0.5); 

	return ptRes;
}

void CLineObject::GetArrow(LPPOINT ptArrow, DWORD dwArraw, CPoint ptStart,
	CPoint ptEnd)
{
	if (dwArraw & DA_BEGIN)
	{
		ptArrow[0] = ptStart;
		ptArrow[1] = ptEnd;
	}
	else
	{
		ptArrow[0] = ptEnd;
		ptArrow[1] = ptStart;
	}

	CPoint tmp = ptArrow[0];
	CPoint ptx;
	if (ptStart.x == ptEnd.x)
	{
		tmp.y = ptArrow[0].y +
			(ptArrow[1].y >= ptArrow[0].y ? m_iArrowSize : -m_iArrowSize); 
		ptArrow[1].x = tmp.x - m_iArrowSize / 2; 
		ptArrow[1].y = tmp.y; 
		ptArrow[2].x = tmp.x + m_iArrowSize / 2; 
		ptArrow[2].y = tmp.y;
		ptx.x = tmp.x;
		ptx.y = (ptArrow[0].y + tmp.y) / 2;
	}
	else
	{
		double K = (double) (ptArrow[1].y - ptArrow[0].y) /
			(ptArrow[1].x - ptArrow[0].x); 
		tmp = GetPointByLength(ptArrow[0], K,
				(ptArrow[1].x >= ptArrow[0].x) ? m_iArrowSize : -m_iArrowSize);
		if (dwArraw & DA_X)
			ptx = GetPointByLength(ptArrow[0], K,
					(ptArrow[1].x >= ptArrow[0].x) ?
					m_iArrowSize /
					2 :
					-
					m_iArrowSize /
					2);
		if (K == 0)
		{
			ptArrow[1].x = tmp.x; 
			ptArrow[1].y = tmp.y - m_iArrowSize / 2; 
			ptArrow[2].x = tmp.x; 
			ptArrow[2].y = tmp.y + m_iArrowSize / 2;
		}
		else
		{
			K = -1 / K; 
			ptArrow[1] = GetPointByLength(tmp, K, m_iArrowSize / 2); 
			ptArrow[2] = GetPointByLength(tmp, K, -m_iArrowSize / 2);
		}
	}

	if (dwArraw & DA_AKX)
	{
		tmp = ptArrow[0];
		ptArrow[0] = ptArrow[1];
		ptArrow[1] = tmp;
		if (dwArraw & DA_K)
			ptArrow[3] = ptArrow[0];
		else if (dwArraw & DA_X)
			ptArrow[3] = ptx;
	}
}

int CLineObject::HitTest(CPoint point, CDC* pDC, CRectTracker* pRectTracker)
{
	pDC->DPtoLP(&point);
	return HitTestLine(point, m_rectPosition.TopLeft(), m_rectPosition.BottomRight());
}

void CLineObject::GetHandleRect(int nHandle, CRect* pHandleRect, CDC* pDC)
{
	if (nHandle == 1)
		nHandle = 2;

	CMRTObject::GetHandleRect(nHandle, pHandleRect, pDC);
}

CPolylineObject::CPolylineObject()
{
}

CPolylineObject::~CPolylineObject()
{
}

void CPolylineObject::Serialize(CXMLSettings& xml, DWORD dwProp /* = 0xFFFFFFFF */)
{
	CLineObject::Serialize(xml, dwProp);
	if (xml.IsStoring())
	{
		xml.Write(_T("Points"), m_points);
	}
	else
	{
		xml.Read(_T("Points"), m_points);
	}
}

void CPolylineObject::Draw(CDC* pDC, CRect& rect)
{
	unsigned pattern[8];
	int w = pDC->IsPrinting() ? LPWidth(m_iLineWidth) : LVWidth(m_iLineWidth);
	CDashLine dl(*pDC, pattern, CDashLine::GetPattern(pattern, 0, w,
									m_iLineStyle));
	CPen* pPen = dl.GetRightPen(m_iLineStyle, w, m_clrLine);
	CPen* pOldPen = pDC->SelectObject(pPen);

	CPoint ptOffset = rect.TopLeft();
// 	ptOffset.x -= m_rectPosition.left;
// 	ptOffset.y -= m_rectPosition.top;

	CPoint ptStart = m_points[0];
	ptStart.x += ptOffset.x;
	ptStart.y += ptOffset.y;
	dl.MoveTo(ptStart);

	CPoint ptStart0 = ptStart;
	CPoint ptEnd0 = ptStart;
	CPoint ptEnd = ptStart;
	int i = 1;
	for ( ; i < m_points.GetSize(); i++)
	{
		ptEnd = m_points[i];
		ptEnd.x += ptOffset.x;
		ptEnd.y += ptOffset.y;
		dl.LineTo(ptEnd);
		if (i == 1)
			ptEnd0 = ptEnd;
	}

	if (i > 2)
	{
		ptStart = m_points[i - 2];
		ptStart.x += ptOffset.x;
		ptStart.y += ptOffset.y;
	}

	if (m_dwArrow)
	{
		CArray<CPoint, CPoint> ptArrow;
		ptArrow.SetSize((m_dwArrow & DA_KX) ? 4 : 3);

		CPen pen(PS_SOLID, 1, m_clrLine);
		if (w > 8)
			m_iArrowSize = 40;
		else if (w > 4)
			m_iArrowSize = 30;
		else
			m_iArrowSize = 20;

		pDC->SelectObject(&pen);
		CBrush brArrow(m_clrLine);
		CBrush* pOldBr = pDC->SelectObject(&brArrow);
		for (int i = 1; i < 3; i++)
		{
			if (m_dwArrow & i)
			{
				DWORD dwArrow = i + DA_V;
				if (i == 1)
					GetArrow(ptArrow.GetData(), dwArrow, ptStart0, ptEnd0);
				else
					GetArrow(ptArrow.GetData(), dwArrow, ptStart, ptEnd);
				if (m_dwArrow & DA_AK)
					pDC->Polyline(ptArrow.GetData(), ptArrow.GetSize());
				else
					pDC->Polygon(ptArrow.GetData(), ptArrow.GetSize());
			}
		}

		pDC->SelectObject(pOldBr);
	}

	pDC->SelectObject(pOldPen);
	delete pPen;
}

void CPolylineObject::DrawTracker(CDC* pDC, CRect& rect)
{
	int nMM = pDC->SetMapMode(MM_LOMETRIC);

	CRect rectLP = rect;
	pDC->DPtoLP(&rectLP);

	CPoint ptOffset = rectLP.TopLeft();
// 	ptOffset.x -= m_rectPosition.left;
// 	ptOffset.y -= m_rectPosition.top;

	CPoint ptStart = m_points[0];
	ptStart.x += ptOffset.x;
	ptStart.y += ptOffset.y;
	pDC->MoveTo(ptStart);
	
	CPoint ptEnd = ptStart;
	for (int i = 1; i < m_points.GetSize(); i++)
	{
		ptEnd = m_points[i];
		ptEnd.x += ptOffset.x;
		ptEnd.y += ptOffset.y;
		pDC->LineTo(ptEnd);
	}

	pDC->SetMapMode(nMM);
}

int CPolylineObject::HitTest(CPoint point, CDC* pDC, CRectTracker* pRectTracker)
{
	pDC->DPtoLP(&point);

	int iHitResult = -1;
	CPoint ptStart = m_points[0];
	ptStart.x += m_rectPosition.left;
	ptStart.y += m_rectPosition.top;

	CPoint ptEnd;
	BOOL bHitMiddle = FALSE;
	for (int i = 1; i < m_points.GetSize(); i++)
	{
		ptEnd = m_points[i];
		ptEnd.x += m_rectPosition.left;
		ptEnd.y += m_rectPosition.top;
		iHitResult = HitTestLine(point, ptStart, ptEnd);
		if (iHitResult != -1)
		{
			if (iHitResult != 8) // hit middle
			{
				iHitResult = (iHitResult == 2) ? i : (i - 1);
				iHitResult += 9;
				return iHitResult;
			}
			else
			{
				bHitMiddle = TRUE;
			}
		}
		ptStart = ptEnd;
	}

	return bHitMiddle ? 8 : -1;
}

void CPolylineObject::GetHandleRect(int nHandle, CRect* pHandleRect, CDC* pDC)
{
	CPoint point = m_points.GetAt(nHandle);
// 	point.x += m_rectPosition.left;
// 	point.y += m_rectPosition.top;
	
	int nMM = pDC->SetMapMode(MM_LOMETRIC);
	pDC->LPtoDP(&point);
	pDC->SetMapMode(nMM);

	point.x += pHandleRect->left;
	point.y += pHandleRect->top;

	CRect rectT(point, point);

	rectT.NormalizeRect();
	// if ((m_nStyle & (solidLine|dottedLine)) != 0)
		rectT.InflateRect(+1, +1);

	// since the rectangle itself was normalized, we also have to invert the
	//  resize handles.
	// nHandle = NormalizeHit(nHandle);

	// handle case of resize handles outside the tracker
	int size = 5;
	rectT.InflateRect((size-1)/2, (size-1)/2);

	// calculate position of the resize handle
	int nWidth = rectT.Width();
	int nHeight = rectT.Height();
	CRect rect;
	const AFX_HANDLEINFO* pHandleInfo = &_afxHandleInfo[0];
	rect.left = *(int*)((BYTE*)&rectT + pHandleInfo->nOffsetX);
	rect.top = *(int*)((BYTE*)&rectT + pHandleInfo->nOffsetY);
	rect.left += size * pHandleInfo->nHandleX;
	rect.top += size * pHandleInfo->nHandleY;
	rect.left += pHandleInfo->nCenterX * (nWidth - size) / 2;
	rect.top += pHandleInfo->nCenterY * (nHeight - size) / 2;
	rect.right = rect.left + size;
	rect.bottom = rect.top + size;

	*pHandleRect = rect;
}

void CPolylineObject::SetRect(LPCRECT lpSrcRect)
{
	if (!IsLocked())
	{
		CRect rect = m_rectPosition;
		m_rectPosition = lpSrcRect;
		
		double fX = (double) abs(m_rectPosition.Width()) /
			(double) abs(rect.Width());
		
		double fY = (double) abs(m_rectPosition.Height()) /
			(double) abs(rect.Height());

		for (int i = 0; i < m_points.GetSize(); i++)
		{
			m_points[i].x = floor((double)m_points[i].x * fX + .5);
			m_points[i].y = floor((double)m_points[i].y * fY + .5);
		}
	}
}

CPolygonObject::CPolygonObject()
{
	m_dwProp = 0x0050;
	m_clrFill = RGB(0, 0, 0); //RGB(192,192,192);
	m_clrBkg = RGB(255,255,255);
}

CPolygonObject::~CPolygonObject()
{

}

void CPolygonObject::Serialize(CXMLSettings& xml, DWORD dwProp /* = 0xFFFFFFFF */)
{
	CPolylineObject::Serialize(xml, dwProp);
	if (xml.IsStoring())
	{
		xml.Write(_T("Pattern"), m_strPat);
		xml.Write(_T("FillColor"), m_clrFill);
		xml.Write(_T("BkgColor"), m_clrBkg);
	}
	else
	{
		if (dwProp & 0x0040)
		{
			xml.Read(_T("Pattern"), m_strPat);
			xml.Read(_T("FillColor"), m_clrFill);
			xml.Read(_T("BkgColor"), m_clrBkg);
		}
	}
}

void CPolygonObject::Draw(CDC* pDC, CRect& rect)
{
	unsigned pattern[8];
	int w = pDC->IsPrinting() ? LPWidth(m_iLineWidth) : LVWidth(m_iLineWidth);
	CDashLine dl(*pDC, pattern, CDashLine::GetPattern(pattern, 0, w,
									m_iLineStyle));
	CPen* pPen = dl.GetRightPen(m_iLineStyle, w, m_clrLine);
	CPen* pOldPen = pDC->SelectObject(pPen);

	CBrush* pOldBr = NULL;
	CBrush* pBrush = NULL;
	if (m_clrFill != (COLORREF) - 1)
	{
		pBrush = new CBrush(m_clrFill);
		pOldBr = pDC->SelectObject(pBrush);
	}
	else
	{
		pOldBr = (CBrush *) pDC->SelectStockObject(NULL_BRUSH);
	}

	CPoint ptOffset = rect.TopLeft();
	CArray<CPoint,CPoint> points;
	points.Copy(m_points);
	int i = 0;
	for (; i < points.GetSize(); i++)
	{
		points[i].x += ptOffset.x;
		points[i].y += ptOffset.y;
	}

	if (m_strPat.GetLength())
	{
		pDC->BeginPath();
		pDC->Polygon(points.GetData(), points.GetSize());
		pDC->EndPath();

		CRgn rgn;
		if (rgn.CreateFromPath(pDC))
		{
			pDC->SelectClipRgn(&rgn);
			DrawPattern(pDC, rect, m_strPat, m_clrFill, m_clrBkg);
			pDC->SelectClipRgn(NULL);
		}
	}

	dl.MoveTo(points[0]);
	for (i = 1; i < m_points.GetSize(); i++)
	{
		dl.LineTo(points[i]);
	}
	dl.LineTo(points[0]);

	pDC->SelectObject(pOldBr);
	pDC->SelectObject(pOldPen);
	delete pPen;
	if (pBrush)
		delete pBrush;
}

void CPolygonObject::DrawTracker(CDC* pDC, CRect& rect)
{
	int nMM = pDC->SetMapMode(MM_LOMETRIC);

	CRect rectLP = rect;
	pDC->DPtoLP(&rectLP);

	CPoint ptOffset = rectLP.TopLeft();

	CArray<CPoint,CPoint> points;
	points.Copy(m_points);
	for (int i = 0; i < points.GetSize(); i++)
	{
		points[i].x += ptOffset.x;
		points[i].y += ptOffset.y;
	}
	pDC->Polygon(points.GetData(), points.GetSize());
	
	pDC->SetMapMode(nMM);
}

int CPolygonObject::HitTest(CPoint point, CDC* pDC, CRectTracker* pRectTracker)
{
	pDC->DPtoLP(&point);

	CRect rect = m_rectPosition;
	rect.InflateRect(10, -10, 10, -10);
	rect.NormalizeRect();
	if (!rect.PtInRect(point))
		return -1;

	int iHitResult = -1;
	CArray<CPoint, CPoint> points;
	points.Copy(m_points);
	int i = 0;
	for (; i < points.GetSize(); i++)
	{
		points[i].x += m_rectPosition.left;
		points[i].y += m_rectPosition.top;
	}

	CPoint ptStart = m_points[0];
	CPoint ptEnd;
	BOOL bHitMiddle = FALSE;
	for (/*int */i = 1; i < points.GetSize(); i++)
	{
		ptEnd = points[i];
		iHitResult = HitTestLine(point, ptStart, ptEnd);
		if (iHitResult != -1)
		{
			if (iHitResult != 8) // hit middle
			{
				iHitResult = (iHitResult == 2) ? i : (i - 1);
				iHitResult += 9;
				return iHitResult;
			}
			else
				bHitMiddle = TRUE;
			
		}
		ptStart = ptEnd;
	}

	if (i > 2)
	{
		ptEnd = points[0];
		iHitResult = HitTestLine(point, ptStart, ptEnd);
		if (iHitResult != -1)
		{
			if (iHitResult != 8) // hit middle
			{
				iHitResult = (iHitResult == 2) ? 0 : (i - 1);
				iHitResult += 9;
				return iHitResult;
			}
			else
				bHitMiddle = TRUE;
		}
	}

	if (bHitMiddle)
		return 8;

	CRgn rgn;
	rgn.CreatePolygonRgn(points.GetData(), points.GetSize(), WINDING);
	if (rgn.PtInRegion(point))
		return 8;

	return -1;
}

IMPLEMENT_SERIAL(CRectObject, CLineObject, 0)

CRectObject::CRectObject()
{
	InitRectObject();
}

CRectObject::~CRectObject()
{
	if (m_pFont)
	{
		delete m_pFont;
		m_pFont = NULL;
	}

	if (m_pDPFont)
	{
		delete m_pDPFont;
		m_pDPFont = NULL;
	}
}

void CRectObject::InitRectObject()
{
	m_dwProp = 0x0070;

    m_bUsePattern = TRUE; 
	m_clrText = RGB(0, 0, 0);
	m_clrBk = (COLORREF) - 1;
	m_pFont = NULL;
	m_pDPFont = NULL;
	
	m_clrFill = RGB(0, 0, 0); //RGB(192,192,192);
	m_clrBkg = RGB(255,255,255);
	m_lHorz = 1;
	m_lVert = 0;

	m_lHorzPic = 0;
	m_lVertPic = 0;
	m_iSizeStyle = 0;

	m_ptOffset.x = 0;
	m_ptOffset.y = 0;
	m_iWidthPic = 0;
	m_iHeightPic = 0;
}

#define sp_x 5

CRect DrawTextInRect(CDC* pDC, CString szString, LPRECT lpRect, long lHori,
	long lVert)
{
	CRect rcInner(lpRect);
	if (rcInner.Width() == 0)
		return rcInner;

	pDC->IntersectClipRect(rcInner);

	int iWidth0 = rcInner.Width();
	int iHeight0 = rcInner.Height();

	CFont* pFont = pDC->GetCurrentFont();
	LOGFONT lf;
	ZeroMemory(&lf, sizeof(LOGFONT));
	pFont->GetLogFont(&lf);

	UINT nTA = pDC->GetTextAlign();

	CStringArray strArray;

	rcInner.left += sp_x;
	rcInner.right -= sp_x;
	rcInner.top -= sp_x;
	rcInner.bottom += sp_x;

	// Calculate line here
	int iLineWidth = (lf.lfEscapement % 1800) ?
		abs(rcInner.Height()) :
		abs(rcInner.Width());

	szString.Replace(_T("\r\n"), _T("\n"));
	int iStrLen = szString.GetLength();

	int nCount;
	int iLineMaxWidth = 0;
	for (int iFrom = 0; iFrom < szString.GetLength();)
	{
		int iEnd = szString.Find(_T('\n'), iFrom);
		if (iEnd < 0)
			iEnd = szString.GetLength();

		nCount = iEnd - iFrom;
		CString strSubStr = szString.Mid(iFrom, nCount);
		CSize szText = pDC->GetTextExtent(strSubStr);

		if (szText.cx > iLineWidth)
		{
			do
			{
				CString strA;
				int i = 0;
				for ( ; i < nCount; i++)
				{
					unsigned char c = strSubStr[i];
					if (c >= 0xA1)
						strA = strSubStr.Left(i + 2);
					else
						strA = strSubStr.Left(i + 1);

					szText = pDC->GetTextExtent(strA);
					if (szText.cx > iLineWidth)
						break;

					if (c >= 0xA1)
						i++;
				}

				if (i == 0)
				{
					i = 1;
					unsigned char c = strSubStr[0];
					if (c >= 0xA1)
						strA = strSubStr.Left(++i);
					else
						strA = strSubStr.Left(i);
					szText = pDC->GetTextExtent(strA);
				}
				else if (i < nCount)
				{
					strA = strSubStr.Left(i);
					szText = pDC->GetTextExtent(strA);
				}

				strArray.Add(strA);
				if (szText.cx > iLineMaxWidth)
					iLineMaxWidth = szText.cx;

				nCount -= i;				
				strSubStr = strSubStr.Right(nCount);
				szText = pDC->GetTextExtent(strSubStr);
			}
			while (szText.cx > iLineWidth);

			if (strSubStr.GetLength())
			{
				szText = pDC->GetTextExtent(strSubStr);
				strArray.Add(strSubStr);
				if (szText.cx > iLineMaxWidth)
					iLineMaxWidth = szText.cx;
			}
		}
		else
		{
			strArray.Add(strSubStr);
			if (szText.cx > iLineMaxWidth)
				iLineMaxWidth = szText.cx;
		}

		iFrom = iEnd + 1;
	}

	switch (lf.lfEscapement)
	{
	case 0:
		pDC->SetTextAlign(TA_LEFT);
		break;
	case 900:
		pDC->SetTextAlign(TA_LEFT);
		break;
	case 1800:
		pDC->SetTextAlign(TA_RIGHT);
		break;
	case 2700:
		pDC->SetTextAlign(TA_RIGHT);
		break;
	default:
		break;
	}

	nCount = strArray.GetSize();

	TEXTMETRIC tm;
	pDC->GetTextMetrics(&tm);

	CRect rcLine = rcInner;
	for (int i = 0; i < nCount; i++)
	{
		// TRACE("Str%d : %s\n", i, strArray[i]);
		szString = strArray[i];
		pDC->DrawText(szString, rcLine, DT_CALCRECT);
		int iWidth = rcLine.Width();
		int iHeight = rcLine.Height();

		switch (lHori)
		{
		case 0:
			if (lf.lfEscapement == 900)
			{
				rcLine.left = rcInner.left - (nCount - i) * iHeight;
			}
			else if (lf.lfEscapement == 2700)
			{
				rcLine.left = rcInner.left - i * iHeight;
			}
			break;
		case 1:
			if (lf.lfEscapement == 900)
			{
				// int iW = -iHeight*nCount;
				// rcLine.left = lpRect->left + (iWidth0 - iW)/2 - (nCount-i)*iHeight;
				rcLine.left = lpRect->left +
					(iWidth0 - iHeight * nCount) /
					2 +
					i * iHeight;
			}
			else if (lf.lfEscapement == 2700)
			{
				// int iW = iHeight*nCount;
				// rcLine.left = lpRect->left + (iWidth0 - iW)/2 + (nCount-i)*iHeight;
				rcLine.left = lpRect->left +
					(iWidth0 + iHeight * nCount) /
					2 -
					i * iHeight;
			}
			else
			{
				rcLine.left = lpRect->left + (iWidth0 - iWidth) / 2;
			}
			break;
		case 2:
			if (lf.lfEscapement == 900)
			{
				rcLine.left = rcInner.right + i * iHeight;
			}
			else if (lf.lfEscapement == 2700)
			{
				// int iW = -iHeight*nCount;
				// rcLine.left = rcInner.right - iW - i*iHeight;
				rcLine.left = rcInner.right + (nCount - i) * iHeight;
			}
			else
			{
				rcLine.left = rcInner.right - iWidth;
			}
			break;
		default:
			break;
		}

		switch (lVert)
		{
		case 0:
			if (lf.lfEscapement % 1800)
			{
				rcLine.top = rcInner.top;
			}
			else if (lf.lfEscapement == 1800)
			{
				rcLine.top = rcInner.top + (nCount - i) * iHeight;
			}
			break;
		case 1:
			if (lf.lfEscapement == 0)
			{
				// int iH = -iHeight*nCount;
				// rcLine.top = lpRect->top + (iHeight0 - iH)/2 - (nCount-i)*iHeight;
				rcLine.top = lpRect->top +
					(iHeight0 - iHeight * nCount) /
					2 +
					i * iHeight;
			}
			else if (lf.lfEscapement == 1800)
			{
				// int iH = iHeight*nCount;
				// rcLine.top = lpRect->top + (iHeight0 - iH)/2 + (nCount-i)*iHeight;
				rcLine.top = lpRect->top +
					(iHeight0 + iHeight * nCount) /
					2 -
					i * iHeight;
			}
			else
			{
				rcLine.top = lpRect->top + (iHeight0 + iWidth) / 2;
			}
			break;
		case 2:
			if (lf.lfEscapement == 0)
			{
				rcLine.top = rcInner.bottom - (nCount - i) * iHeight;
			}
			else if (lf.lfEscapement == 1800)
			{
				rcLine.top = rcInner.bottom - i * iHeight;
			}
			else
			{
				rcLine.top = rcInner.bottom + iWidth;
			}
			break;
		default:
			break;
		}

		rcLine.right = rcLine.left + iWidth;
		rcLine.bottom = rcLine.top + iHeight;

		//---------------------------------------------------------------------------------------------
		pDC->DrawText(szString, rcLine, DT_WORDBREAK | DT_LEFT | DT_NOCLIP);

		if (lf.lfEscapement == 0)
			rcLine.top = rcLine.bottom;
	}

	pDC->SetTextAlign(nTA);
	pDC->SelectClipRgn(NULL);
	return rcInner;
}

void CRectObject::Serialize(CXMLSettings& xml, DWORD dwProp /* = 0xFFFFFFFF */)
{
	CLineObject::Serialize(xml, dwProp);

	if (xml.IsStoring())
	{
		xml.Write(_T("TextColor"), m_clrText);
		xml.Write(_T("BkColor"), m_clrBk);
		xml.Write(_T("Horz"), m_lHorz);
		xml.Write(_T("Vert"), m_lVert);

		LOGFONT lf;
		ZeroMemory(&lf, sizeof(LOGFONT));
		if (m_pFont)
		{
			m_pFont->GetLogFont(&lf);
			xml.Write(_T("Font"), (LPBYTE) & lf, sizeof(LOGFONT));
		}
        
        xml.Write(_T("UsePattern"), m_bUsePattern); 
		xml.Write(_T("Pattern"), m_strPat);
		xml.Write(_T("FillColor"), m_clrFill);
		xml.Write(_T("BkgColor"), m_clrBkg);
		xml.Write(_T("Picture"), m_strFile);
		xml.Write(_T("PicSize"), m_iSizeStyle);
		xml.Write(_T("PicHorz"), m_lHorzPic);
		xml.Write(_T("PicVert"), m_lVertPic);
		xml.Write(_T("PicW"), m_iWidthPic);
		xml.Write(_T("PicH"), m_iHeightPic);
		xml.Write(_T("PicOffset"), m_ptOffset);
	}
	else
	{
		if (dwProp & 0x0020)
		{
			xml.Read(_T("TextColor"), m_clrText);
			xml.Read(_T("BkColor"), m_clrBk);
			xml.Read(_T("Horz"), m_lHorz);
			xml.Read(_T("Vert"), m_lVert);

			LOGFONT lf;
			ZeroMemory(&lf, sizeof(LOGFONT));
			UINT nSize;
			xml.Read(_T("Font"), (LPBYTE) & lf, nSize);
			if (nSize == sizeof(LOGFONT))
			{
				if (m_pFont)
					delete m_pFont;
				m_pFont = new CFont;
				m_pFont->CreateFontIndirect(&lf);
				if (m_pDPFont)
				{
					delete m_pDPFont;
					m_pDPFont = NULL;
				}
			}
		}

		if (dwProp & 0x0040)
		{
            xml.Read(_T("UsePattern"), m_bUsePattern); 
			xml.Read(_T("Pattern"), m_strPat);
			xml.Read(_T("FillColor"), m_clrFill);
			xml.Read(_T("BkgColor"), m_clrBkg);
			xml.Read(_T("Picture"), m_strFile);
			if (m_strFile.GetLength())
				LoadPicture(m_strFile);
			else
				FreePicture();

			xml.Read(_T("PicSize"), m_iSizeStyle);
			xml.Read(_T("PicHorz"), m_lHorzPic);
			xml.Read(_T("PicVert"), m_lVertPic);
			xml.Read(_T("PicW"), m_iWidthPic);
			xml.Read(_T("PicH"), m_iHeightPic);
			xml.Read(_T("PicOffset"), m_ptOffset);
		}
	}
}

void CRectObject::CreatePicture(LPCTSTR pszFile, HBITMAP hBitmap)
{
	HBITMAP hBmp = LoadPicture((HBITMAP) hBitmap);
	if (hBmp == NULL)
	{
		FreePicture();
		return;
	}

	CFile file;
	if (!file.Open(pszFile, CFile::modeWrite | CFile::modeCreate))
		return;

	// Write the DIB header and the bits 
	file.Write((LPBYTE) GlobalLock(hGlobal), GlobalSize(hGlobal));
	GlobalUnlock(hGlobal);
	m_strFile = pszFile;
}

CFont* CRectObject::SelectFont(CDC* pDC)
{
    CFont* pOldFont = NULL;
	if (m_pFont != NULL)
	{
		pOldFont = pDC->SelectObject(m_pFont);
		if (m_pDPFont == NULL)
		{
			LOGFONT lf;
			ZeroMemory(&lf, sizeof(LOGFONT));
			m_pFont->GetLogFont(&lf);
			CSize szText(lf.lfWidth, lf.lfHeight);
			pDC->LPtoDP(&szText);
			lf.lfHeight = szText.cy;
			lf.lfWidth = szText.cx;
			lf.lfEscapement = 0;
			m_pDPFont = new CFont;
			m_pDPFont->CreateFontIndirect(&lf);
		}
	}
	else
	{
		LOGFONT lf;
		ZeroMemory(&lf, sizeof(LOGFONT));
		CFont* pFont = pDC->GetCurrentFont();
		pFont->GetLogFont(&lf);
		m_pFont = new CFont;
		m_pFont->CreateFontIndirect(&lf);

		CSize szText(lf.lfWidth, lf.lfHeight);
		pDC->LPtoDP(&szText);
		lf.lfHeight = szText.cy;
		lf.lfWidth = szText.cx;
		lf.lfEscapement = 0;
		if (m_pDPFont)
			delete m_pDPFont;
		m_pDPFont = new CFont;
		m_pDPFont->CreateFontIndirect(&lf);
	}

	return pOldFont;
}

void CRectObject::Draw(CDC* pDC, CRect& rect)
{
	COLORREF clrBk = pDC->GetBkColor();

	// Draw Rect
	CPen* pOldPen = NULL;
	CPen* pPen = NULL;

	unsigned pattern[8];
	int w = pDC->IsPrinting() ? LPWidth(m_iLineWidth) : LVWidth(m_iLineWidth);
	CDashLine dl(*pDC, pattern, CDashLine::GetPattern(pattern, 0, w,
									m_iLineStyle));

	if (m_iLineWidth > 0)
	{
		pPen = dl.GetRightPen(m_iLineStyle, w, m_clrLine);
		// pPen = new CPen(m_iLineStyle, w, m_clrLine);
		pOldPen = pDC->SelectObject(pPen);
	}
	else
	{
		pOldPen = (CPen *) pDC->SelectStockObject(NULL_PEN);
	}

	CBrush* pOldBr = NULL;
	CBrush* pBrush = NULL;
	if (m_strPat.GetLength())
	{
//		pBrush = new CBrush;
// 		pOldBr = pDC->SelectObject(pBrush);
//		pOldBr = SelectPatternBrush(pDC, pBrush, m_strPat, m_clrFill, m_clrBkg);
	}
	else
	{
		pOldBr = (CBrush *) pDC->SelectStockObject(NULL_BRUSH);
	}

	// Draw Picture
	if (!m_bUsePattern && m_strFile.GetLength())
	{
		int w = -1;
		int h = -1;
		if (m_iSizeStyle == 1)
		{
			w = rect.Width();
			h = rect.Height();
		}
		else if (m_iSizeStyle == 2)
		{
			w = m_iWidthPic;
			h = -m_iHeightPic;
		}
		else
		{
			CWindowDC dc(NULL);
			int nMapMode = dc.SetMapMode(MM_LOMETRIC);
			CSize szText(_GetWidth(), _GetHeight());
			dc.DPtoLP(&szText);
			dc.SetMapMode(nMapMode);
			w = szText.cx;
			h = -szText.cy;
		}

		int x = rect.left; 
		int y = rect.top;
		if (m_lHorzPic == 1)
		{
			x = rect.left + (rect.Width() - w) / 2;
		}
		else if (m_lHorzPic == 2)
		{
			x = rect.right - w;
		}

		if (m_lVertPic == 1)
		{
			y = rect.top + (rect.Height() - h) / 2;
		}
		else if (m_lVertPic == 2)
		{
			y = rect.bottom - h;
		}

		x += m_ptOffset.x;
		y -= m_ptOffset.y;
		CMyMemDC dc(pDC, &rect);
		// dc.m_dwRop = SRCAND;
		CString strExt = m_strFile.Right(4);
		if (strExt.CompareNoCase(_T(".wmf")))
		{
			if (strExt.CompareNoCase(_T(".emf")))
				DrawPicture(dc.GetSafeHdc(), x, y, w, h);
			else
			{
				// emf offset x, y
				CRect rect0(0, 0, rect.right, rect.bottom);
				CMyMemDC dc0(&dc, &rect0);
				DrawPicture(dc0.GetSafeHdc(), x, y, w, h);
			}
		}
		else
		{
#ifndef _TOOLMAKER
			CMetaFile mf;
			mf.Read(m_strFile);
			CRect rcBounds = CRect(CPoint(x, y), CSize(w, h));
			mf.Display(&dc, rcBounds);
#endif
		}
	}

	if (m_bUsePattern && m_strPat.GetLength())
	{
		if (m_strPat.Right(4).CompareNoCase(_T(".rmk")))
		{
			DrawPattern(pDC, rect, m_strPat, m_clrFill, m_clrBkg);
		}
		else
		{
			CMyMemDC dc(pDC, &rect);
			// dc.FillRect(rect, pBrush);
			DrawPattern(&dc, rect, m_strPat, m_clrFill, m_clrBkg);
		}
	}

	// Draw Rect Later
	if ((m_iLineWidth > 0) || (m_clrFill != (COLORREF) - 1))
	{
		// pDC->Rectangle(rect);
		dl.MoveTo(rect.left, rect.top);
		dl.LineTo(rect.right /*- 1*/, rect.top);
		dl.LineTo(rect.right /*- 1*/, rect.bottom /*+ 1*/);
		dl.LineTo(rect.left, rect.bottom /*+ 1*/);
		dl.LineTo(rect.left, rect.top);
	}

	// Draw Text
	CFont* pOldFont = SelectFont(pDC);

	int nBkMode = pDC->GetBkMode();
	if (m_clrBk != (COLORREF) - 1)
		pDC->SetBkColor(m_clrBk);
	else
		pDC->SetBkMode(TRANSPARENT);

	COLORREF clrText = pDC->SetTextColor(m_clrText);
	DrawTextInRect(pDC, m_strText, rect, m_lHorz, m_lVert);
	pDC->SetBkColor(clrBk);
	pDC->SetBkMode(nBkMode);
	pDC->SetTextColor(clrText);
	if (pOldFont)
		pDC->SelectObject(pOldFont);
	if (pOldPen)
		pDC->SelectObject(pOldPen);
	if (pOldBr)
		pDC->SelectObject(pOldBr);
	if (pPen)
		delete pPen;
	if (pBrush)
		delete pBrush;
}

CEdit* CRectObject::CreateInPlaceEdit(CRect rectEdit, CWnd* pWnd)
{
	DWORD dwAlign = m_lHorz & 0x01;
	
	CFont* pFont = GetDPFont();
	int nMinHeight = 20;
	int nMinWidth = 80;
	if (pFont != NULL)
	{
		LOGFONT lf;
		ZeroMemory(&lf, sizeof(LOGFONT));
		pFont->GetLogFont(&lf);
		if (abs(lf.lfHeight) > nMinHeight)
		{
			nMinHeight = abs(lf.lfHeight);
			nMinWidth = nMinHeight*4;
		}
	}

	rectEdit.NormalizeRect();
	if (rectEdit.Width() < nMinWidth)
		rectEdit.right = rectEdit.left + nMinWidth;
	if (rectEdit.Height() < nMinHeight)
		rectEdit.bottom = rectEdit.top + nMinHeight;
	
	CEdit* pEdit = new CEdit;
	pEdit->Create(WS_CHILD |
			WS_VISIBLE |
			ES_AUTOHSCROLL |   /*ES_WANTRETURN|*/
			ES_MULTILINE |
			dwAlign |
			ES_AUTOVSCROLL|
			WS_BORDER, rectEdit, pWnd, 0x0480);
	
	if (pFont != NULL)
		pEdit->SetFont(pFont);

	pEdit->SetWindowText(m_strText);
	pEdit->SetFocus();
	pEdit->SetSel(0, -1);
	return pEdit;
}

void CEllipseObject::Draw(CDC* pDC, CRect& rect)
{
	COLORREF clrBk = pDC->GetBkColor();

	// Draw Rect
	CPen* pOldPen = NULL;
	CPen* pPen = NULL;
	unsigned pattern[8];
	int w = pDC->IsPrinting() ? LPWidth(m_iLineWidth) : LVWidth(m_iLineWidth);
	CDashLine dl(*pDC, pattern, CDashLine::GetPattern(pattern, 0, w,
									m_iLineStyle));

	CPoint ptEllipse[13];
	CRgn rgnEllipse;
	if ((m_iLineWidth > 0)||(m_strPat.GetLength())||(m_strFile.GetLength()))
	{
		EllipseToBezier(rect, ptEllipse);
		if ((m_strPat.GetLength())||(m_strFile.GetLength()))
		{
			// Update EllipseRgn
			pDC->BeginPath();
			pDC->PolyBezier(ptEllipse, 13);
			pDC->EndPath();
			
			rgnEllipse.CreateFromPath(pDC);
		}
	}

	if (m_iLineWidth > 0)
	{
		pPen = dl.GetRightPen(m_iLineStyle, w, m_clrLine);
		// pPen = new CPen(m_iLineStyle, w, m_clrLine);
		pOldPen = pDC->SelectObject(pPen);
	}
	else
	{
		pOldPen = (CPen*)pDC->SelectStockObject(NULL_PEN);
	}

	CBrush* pOldBr = NULL;
	CBrush* pBrush = NULL;

	if (m_strPat.GetLength())
	{
		// pBrush = new CBrush;
		// pOldBr = pDC->SelectObject(pBrush);
		// pOldBr = SelectPatternBrush(pDC, pBrush, m_strPat, m_clrFill, m_clrBkg);
	}
	else
	{
		pOldBr = (CBrush *) pDC->SelectStockObject(NULL_BRUSH);
	}

	// Draw Picture
	if (m_strFile.GetLength())
	{
		int w = -1;
		int h = -1;
		if (m_iSizeStyle == 1)
		{
			w = rect.Width();
			h = rect.Height();
		}
		else if (m_iSizeStyle == 2)
		{
			w = m_iWidthPic;
			h = -m_iHeightPic;
		}
		else
		{
			CWindowDC dc(NULL);
			int nMapMode = dc.SetMapMode(MM_LOMETRIC);
			CSize szText(_GetWidth(), _GetHeight());
			dc.DPtoLP(&szText);
			dc.SetMapMode(nMapMode);
			w = szText.cx;
			h = -szText.cy;
		}

		int x = rect.left; 
		int y = rect.top;
		if (m_lHorzPic == 1)
		{
			x = rect.left + (rect.Width() - w) / 2;
		}
		else if (m_lHorzPic == 2)
		{
			x = rect.right - w;
		}

		if (m_lVertPic == 1)
		{
			y = rect.top + (rect.Height() - h) / 2;
		}
		else if (m_lVertPic == 2)
		{
			y = rect.bottom - h;
		}

		x += m_ptOffset.x;
		y -= m_ptOffset.y;

		pDC->SelectClipRgn(&rgnEllipse);
		{
			CMyMemDC dc(pDC, &rect);
			DrawPicture(dc.GetSafeHdc(), x, y, w, h);
		}
		pDC->SelectClipRgn(NULL);		
	}

	// Draw Rect Later
	if ((m_iLineWidth > 0) || (m_strPat.GetLength()))
	{
		if (m_strPat.GetLength())
		{
			// Fill
			pDC->SelectClipRgn(&rgnEllipse);
			// pDC->FillRect(rect, pBrush);

			if (m_strPat.Right(4).CompareNoCase(_T(".rmk")))
			{
				DrawPattern(pDC, rect, m_strPat, m_clrFill, m_clrBkg);
			}
			else
			{
				CMyMemDC dc(pDC, &rect);
				// dc.FillRect(rect, pBrush);
				DrawPattern(&dc, rect, m_strPat, m_clrFill, m_clrBkg);
			}
			
			pDC->SelectClipRgn(NULL);
		}

		// do drawing inside path for win95/8 compatibility
		pDC->BeginPath();
		dl.MoveTo(ptEllipse[0]);
		for (int i = 1; i < 12; i+=3)
			dl.BezierTo(ptEllipse+i);
		pDC->EndPath();
		
		pDC->StrokePath();
		// pDC->Ellipse(rect);
	}

	// Draw Text
	CFont* pOldFont = SelectFont(pDC);

	int nBkMode = pDC->GetBkMode();
	if (m_clrBk != (COLORREF) - 1)
		pDC->SetBkColor(m_clrBk);
	else
		pDC->SetBkMode(TRANSPARENT);

	COLORREF clrText = pDC->SetTextColor(m_clrText);
	DrawTextInRect(pDC, m_strText, rect, m_lHorz, m_lVert);
	pDC->SetBkColor(clrBk);
	pDC->SetBkMode(nBkMode);
	pDC->SetTextColor(clrText);
	if (pOldFont)
		pDC->SelectObject(pOldFont);
	if (pOldPen)
		pDC->SelectObject(pOldPen);
	if (pOldBr)
		pDC->SelectObject(pOldBr);
	if (pPen)
		delete pPen;
	if (pBrush)
		delete pBrush;
}

int CRectObject::HitTest(CPoint point, CDC* pDC, CRectTracker* pRectTracker)
{
	return CMRTObject::HitTest(point, pDC, pRectTracker);
}

